package com.fit.utils.se;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * 单向加密工具(不能将加密前的数据解密出来)
 * 
 * 
 * MD5单向加密
 * SHA单向加密
 * HMAC单向带秘钥加密
 * 
 * 依赖 jar包 : commons-codec-1.10.jar
 * @author hcl
 *
 */
public class OneWayEncryptUtil {
	private static Logger log = LoggerFactory.getLogger(OneWayEncryptUtil.class);
	
	/**
	 * 默认为UTF-8的编码
	 */
	private final static String DEFAULT_CHARACTER_ENCODING = "UTF-8";
	
	
	
    // //////////////////////////////////////////////////////////
    // MD5加密
	// 获取文件md5码依赖: commons-codec-1.10.jar
    // //////////////////////////////////////////////////////////
	
	
	/**
	 * MD5的方式加密数组(16位)
	 * @param b
	 * @return 加密后的byte数组
	 */
	public static byte[] md5(byte[] b)  {
		byte[] result = null;
		MessageDigest digestMd5 = null;
		try {
			digestMd5 = MessageDigest.getInstance("MD5");
			result = digestMd5.digest(b);
		} catch (NoSuchAlgorithmException e) {
			log.error("MD5 加密异常!",e);
			throw new RuntimeException("MD5 加密异常!", e);
		}
		return result;
	}
	
	
	/**
	 * MD5的方式加密字符串(16位)
	 * @param str
	 * @param encoding
	 * @return 加密后的byte数组
	 */
	public static byte[] md5(String str,String encoding) {
		byte[] result = null;
		try {
			result = OneWayEncryptUtil.md5(str.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
			log.error("MD5 加密异常!",e);
			throw new RuntimeException("MD5 加密异常!", e);
		}
		return result;
	}
	
	
	/**
	 * MD5的方式加密字符串(16位)
	 * @param str
	 * @return 加密后的byte数组
	 */
	public static byte[] md5(String str){
		return OneWayEncryptUtil.md5(str, DEFAULT_CHARACTER_ENCODING);
	}
	
	
	
	/**
	 * 获取文件的md5码(16位)
	 * @param file
	 * @return 文件的的md5码,byte 数组
	 */
	public static byte[] md5(File file) {
		byte[] result = null;
		InputStream in = null;
		try {
			in = new FileInputStream(file);
			result = DigestUtils.md5(in);
		}catch(IOException e){
			log.error("MD5 加密异常!",e);
			throw new RuntimeException("MD5 加密异常!", e);
		} finally {
			IOUtil.close(in);
		}
		return result;
	}
	
	
	
	/**
	 * MD5的方式加密字符串,将加密后的字符转换为16进制 和php中MD5加密出来结果一致(32位)
	 * @param bs
	 * @return 返回加密后的字符串
	 */
	public static String md5Hex(byte[] bs) {
		MessageDigest digestMd5 = null;
		String result = null;
		try{
			digestMd5 = MessageDigest.getInstance("MD5");
			digestMd5.update(bs);
			byte[] md = digestMd5.digest();
			BigInteger bi = new BigInteger(1, md);
			// 此方式有误(当转换后 前面出现0的情况(数字中前面不存在0),那么BigInteger会将其自动忽略,所以会有误) (已修复)
			StringBuilder p = new StringBuilder(bi.toString(16));
			int digit = 32 - p.length();
			for (int i = 0; i < digit; i++) {
				p.insert(0, "0");
			}
			result = p.toString();
		}catch(NoSuchAlgorithmException e){
			log.error("MD5加密成16进制 异常!", e);
			throw new RuntimeException("MD5加密成16进制 异常!", e);
		}
		return result;
	}
	
	/**
	 * MD5的方式加密字符串,将加密后的字符转换为16进制 和php中MD5加密出来结果一致(32位)
	 * @param str
	 * @param encoding
	 * @return 返回加密后的字符串
	 */
	public static String md5Hex(String str,String encoding)  {
		String result = null;
		try {
			result = OneWayEncryptUtil.md5Hex(str.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
			log.error("MD5加密成16进制 异常!", e);
			throw new RuntimeException("MD5加密成16进制 异常!", e);
		}
		return result;
	}


	/**
	 * MD5的方式加密字符串,将加密后的字符转换为16进制 和php中MD5加密出来结果一致(32位)
	 * @param str
	 * @return 返回加密后的字符串
	 */
	public static String md5Hex(String str) {
		return OneWayEncryptUtil.md5Hex(str,DEFAULT_CHARACTER_ENCODING);
	}



	/**
	 * 获取文件的md5码(32位)
	 * @param file
	 * @return 文件的的md5码,字符串
	 */
	public static String md5Hex(File file) throws IOException {
		String result = null;
		InputStream in = null;
		try {
			in = new FileInputStream(file);
			result = DigestUtils.md5Hex(in);
		}finally {
			IOUtil.close(in);
		}
		return result;
	}
	
	
	
	// //////////////////////////////////////////////////////////
    // SHA编码
    // //////////////////////////////////////////////////////////

    /**
     * SHA加密(20位)
     * @param data 需要加密的数据
     * @return 加密后的byte数组
     */
    public static byte[] sha(byte[] data) {
    	byte[] result = null;
        MessageDigest md5 = null;
        try {
			md5 = MessageDigest.getInstance("SHA");
			md5.update(data);
	        result =  md5.digest();
		} catch (NoSuchAlgorithmException e) {
			log.error("SHA加密异常!",e);
			throw new RuntimeException("SHA加密异常!", e);
		}
       
        return result;
    }


    /**
     * SHA加密(20位)
     *
     * @param data 需要加密的数据
     * @param encoding 编码
     * @return 加密后的byte数组
     */
    public static byte[] sha(String data,String encoding) {
    	byte[] result = null;
        try {
			result = OneWayEncryptUtil.sha(data.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
			log.error("SHA加密异常!",e);
			throw new RuntimeException("SHA加密异常!", e);
		}
        return result;
    }

    /**
     * SHA加密(20位)
     *
     * @param data     需要加密的数据
     * @return 加密后的byte数组
     */
    public static byte[] sha(String data)  {
		return OneWayEncryptUtil.sha(data, DEFAULT_CHARACTER_ENCODING);
    }


    
    // //////////////////////////////////////////////////////////
    // HMAC带秘钥加密
    // //////////////////////////////////////////////////////////
    /**
     * 获取秘钥，MAC算法可选以下多种算法
     * <p/>
     * <pre>
     * HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384、HmacSHA512
     * </pre>
     */
    public static final String HMAC_KEY_MAC_MD5 = "HmacMD5";
    public static final String HMAC_KEY_MAC_SHA1 = "HmacSHA1";
    public static final String HMAC_KEY_MAC_SHA256 = "HmacSHA256";
    public static final String HMAC_KEY_MAC_SHA384 = "HmacSHA384";
    public static final String HMAC_KEY_MAC_SHA512 = "HmacSHA512";
    
    /**
     * 生成HMAC秘钥(根据key生成一个固定的密钥,长度为8的倍数(具体长度会根据keyMac变化))
     *
     * @param key    秘钥
     * @param kayMac 秘钥算法
     * @return HMAC秘钥 byte数组
     */
    public static byte[] hmacKey(byte[] key, String kayMac)  {
    	byte[] result = null;
		try {
			KeyGenerator kg = KeyGenerator.getInstance(kayMac);
			SecureRandom secureRandom = (null == key || key.length == 0 ) ? new SecureRandom() : new SecureRandom(key);
	        kg.init(secureRandom);
	        SecretKey secretKey = kg.generateKey();
	        result = secretKey.getEncoded();
		} catch (NoSuchAlgorithmException e) {
			log.error("HMAC密钥生成异常!",e);
			throw new RuntimeException("HMAC密钥生成异常!", e);
		}
       
        return result;
    }

    
    /**
     * 生成HMAC秘钥(随机密钥,长度为8的倍数(具体长度会根据keyMac变化))
     *
     * @param kayMac 秘钥算法
     * @return 秘钥 byte数组
     */
    public static byte[] hmacKey(String kayMac) {
        return OneWayEncryptUtil.hmacKey(null, kayMac);
    }
    
    /**
     * 生成HMAC秘钥(根据key生成一个固定的密钥,长度64)
     *
     * @param key 秘钥
     * @return HMAC秘钥 byte数组
     */
    public static byte[] hmacKey(byte[] key) {
		return OneWayEncryptUtil.hmacKey(key, HMAC_KEY_MAC_MD5);
    }
    
    /**
     * 生成HMAC秘钥(随机密钥,长度64)
     * 
     * @return HMAC秘钥 byte数组
     */
    public static byte[] hmacKey() {
		return OneWayEncryptUtil.hmacKey(null, HMAC_KEY_MAC_MD5);
    }


    
    
    
    
    /**
     * HMAC加密(长度为8的倍数(具体长度会根据keyMac变化))
     *
     * @param data     需要加密的字符串
     * @param encoding 需要加密的数据的编码
     * @param key    秘钥
     * @param kayMac 秘钥算法
     * @return 加密后的byte数组
     */
    public static byte[] hmac(String data,String encoding, byte[] key, String kayMac)  {
    	byte[] result = null;
		try {
			SecretKey secretKey = new SecretKeySpec(key, kayMac);
			Mac mac = Mac.getInstance(secretKey.getAlgorithm());
			mac.init(secretKey);
			 result = mac.doFinal(data.getBytes(encoding));
		} catch (Exception e) {
			log.error("HMAC加密异常!",e);
			throw new RuntimeException("HMAC加密异常!", e);
		}
        return result;
    }
    


    /**
     * HMAC加密(指定秘钥算法)(长度为8的倍数(具体长度会根据keyMac变化))
     *
     * @param data   需要加密的字符串
     * @param key    秘钥
     * @param keyMac 秘钥算法
     * @return 加密后的byte数组
     */
    public static byte[] hmac(String data, byte[] key, String keyMac) {
		return OneWayEncryptUtil.hmac(data, DEFAULT_CHARACTER_ENCODING,key, keyMac);
    }
    
    
    /**
     * HMAC加密(采用默认秘钥算法HmacMD5)(16位)
     *
     * @param data 需要加密的字符串
     * @param encoding 编码
     * @param key  秘钥算法
     * @return 加密后的byte数组
     */
    public static byte[] hmac(String data,String encoding, byte[] key)  {
		return OneWayEncryptUtil.hmac(data, encoding,key, HMAC_KEY_MAC_MD5);
    }


    /**
     * HMAC加密(采用默认秘钥算法HmacMD5)(16位)
     *
     * @param data 需要加密的字符串
     * @param key  秘钥算法
     * @return 加密后的byte数组
     */
    public static byte[] hmac(String data,byte[] key) {
		return OneWayEncryptUtil.hmac(data, DEFAULT_CHARACTER_ENCODING,key, HMAC_KEY_MAC_MD5);
    }
}